home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 52
/
Aminet 52 (2002)(GTI - Schatztruhe)[!][Dec 2002].iso
/
Aminet
/
misc
/
emu
/
Apex-src.lha
/
XMODEMA.68K
< prev
next >
Wrap
Text File
|
2001-09-30
|
12KB
|
382 lines
;XMODEMA.68K DEC-04-89 (ALSO SEE "INFOSTR")
;RS-232 handler using X-MODEM protocol
;by Loren Blaney
;
;REVISION HISTORY:
;MAR-15-86, Original
;SEP-14-86, Converted to ASM68K conventions, and modified.
;MAR-09-87, Modified for Amiga's RS-232 interface.
;JUN-14-88, Changed string termination conventions and fixed small error
; in baud rate.
;FEB-15-89, Handle EOTs so it can talk to the rest of the world.
;DEC-04-89, Modified to allow receiving binary files (EOTFLG), increased
; timeout to 10 seconds, and don't bomb if previous block is received twice.
;
;KNOWN BUGS:
; A garbled ACK is fatal. This is not as reliable as it should be.
; Should use serial I/O as a separate device (#4).
; Should this be a unit instead of a device?
;
;NOTES:
;The bytes in the X-modem format are:
; SOH (CTRL-A)
; block count
; one's complement of the block count
; 128 bytes of data
; checksum of data
;
;ACKs and NAKs are used as handshake signals. They are sent by the
; RECEIVING end, and are a request for a block of data to be sent from
; the SENDING end. Initially, the RECEIVING end sends NAKs, indicating
; that it is ready for a block of data. The SENDING end sends the data,
; and if it is correctly received, an ACK is sent back from the
; RECEIVING end. If it is incorrectly received, a NAK is sent back, and
; the SENDING end re-sends the same block of data. When the SENDING end
; is finished, it sends an EOT.
NOLIST
INCLUDE SYSPAG ;Get the system page difinitions
LIST
DEVNUM EQU 5 ;Install this handler as device # 5
;ASCII CHARACTERS:
SOH EQU $01
EOT EQU $04
ACK EQU $06
NAK EQU $15
ORG $7EAC0
START EQU @ ;Address where this handler starts
;=======================================================================
;ENTRY POINTS:
;
XMODEM DC.L OPENI ;$00 = OPENI
DC.L OPENO ;$04 = OPENO
DC.L CHIN ;$08 = CHIN
DC.L CHOUT ;$0C = CHOUT
DC.L CLOSE ;$10 = CLOSE
DC.L GETINFO ;$14 = GETINFO
DC.L DUMMY ;$18 = SPARE
DC.L DUMMY ;$1C = SPARE
;VARIABLES:
INPTR DC.L 0 ;Input buffer byte pointer
OTPTR DC.L 0 ;Output buffer byte pointer
INBUFF DS.B 128 ;Input buffer
INBUFFE EQU @ ;End of input buffer +1
OTBUFF DS.B 128 ;Output buffer
OTBUFFE EQU @ ;End of output buffer +1
INCNT DC.B 0 ;Input block counter
OTCNT DC.B 0 ;Output block counter
ACKNAK DC.B 0 ;Contains an ACK or a NAK
;-----------------------------------------------------------------------
;Open (initialize) the RS-232 port and set the input buffer to empty
;
OPENI MOVE.L D0,-(SP) ;Save D0
MOVE.B #1,INCNT.L ;Init block counter
MOVE.L #INBUFFE,INPTR.L ;Set pointer to empty
CLR.B EOTFLG ;Init EOT flag
BSR OPEN ;Init RS-232 port for I/O, set baud rate
MOVEQ #ACK,D0 ;Kludge to avoid CLOSEI case (Wayne
BSR BYTEOUT ; was write). Send ACK to tell send
; CLOSE routine we're done
BSR FLUSH ;Flush out any bytes in the receive reg
MOVE.B #NAK,ACKNAK.L ;Initially send a NAK
MOVE.L (SP)+,D0 ;Restore D0
DUMMY RTS
;-----------------------------------------------------------------------
;Open (initialize) the RS-232 port and set the output buffer to empty
;
OPENO MOVE.L D0,-(SP) ;Save D0
MOVE.B #1,OTCNT.L ;Init block counter
MOVE.L #OTBUFF,OTPTR.L ;Set pointer to empty
BSR OPEN ;Init RS-232 port for I/O, set baud rate
BSR FLUSH ;Flush out any bytes in the receive reg
OPO10 BSR BYTEIN ;Wait for a NAK
BEQ.S OPO10 ;Wait forever (i.e. no timeout)
CMPI.B #NAK,D0 ;This is required because SENDBUF must
BNE.S OPO10 ; wait for an ACK or NAK at its end
MOVE.L (SP)+,D0 ;Restore D0
RTS
;-----------------------------------------------------------------------
;Get a character from the buffer and return it in D0.
; If the buffer is empty then receive another one.
;
CHIN MOVE.L A6,-(SP) ;Save A6
MOVEA.L INPTR.L,A6 ;Get the buffer pointer
CMPA.L #INBUFFE,A6 ;Is the buffer empty?
BLO.S CHI10 ;Branch if not
BSR GETBUF ;Receive another buffer
TST.L ERRLOC ;Did we get an error?
BNE.S CHI90 ;Branch if yes, exit
MOVEA.L #INBUFF,A6 ;Reset byte pointer to start of buffer
CHI10 MOVEQ #0,D0
MOVE.B (A6)+,D0 ;Get the byte, and increment pointer
MOVE.L A6,INPTR.L ;Save the pointer
CHI90 MOVEA.L (SP)+,A6 ;Restore A6
RTS
;-----------------------------------------------------------------------
;Output the byte in D0 to the buffered RS-232 port
;
CHOUT MOVE.L A6,-(SP) ;Save A6
MOVEA.L OTPTR.L,A6 ;Is there room in the buffer for this
CMPA.L #OTBUFFE,A6 ; byte? i.e. is OTPTR < OTBUFFE?
BLO.S CHO50 ;Branch if yes
BSR SENDBUF ;The buffer is full so send it
TST.L ERRLOC ;Did we get an error?
BNE.S CHO90 ;Branch if yes, exit
MOVEA.L #OTBUFF,A6 ;Reset byte pointer to start of output
; buffer
CHO50 MOVE.B D0,(A6)+ ;Store the byte in the output buffer
MOVE.L A6,OTPTR.L ; bump pointer and save it
CHO90 MOVEA.L (SP)+,A6 ;Restore A6
RTS
;-----------------------------------------------------------------------
;Close the output file
;
CLOSE MOVE.L D0,-(SP) ;Save D0
MOVEQ #EOF,D0 ;Append an EOF to the end of file
BSR CHOUT
MOVE.L OTPTR.L,D0 ;Is there anything in the output buffer?
CMPI.L #OTBUFF,D0
BEQ.S CL20 ;Branch if not
BSR SENDBUF ;Send the output buffer
CL20
MOVEQ #EOT,D0 ;Send an EOT
BSR BYTEOUT
CL90 MOVE.L (SP)+,D0 ;Restore D0
RTS
;-----------------------------------------------------------------------
;Return the address of the information array in D0
;
GETINFO MOVE.L #INFO,D0
RTS
INFO DC.L START ;Start and end addresses of this handler
DC.L END
DC.L INFOSTR ;Description
INFOSTR ASCII 'XMODEMA DEC-04-89 RS-232 handler, 19200 baud'
DC.B 0
;=======================================================================
;SUBROUTINES
;
;Receive a buffer of data from the RS-232 port
;Register usage:
; D0 - I/O
; D1 - Scratch and loop counter
; D2 - Checksum
; A6 - Buffer pointer
;
GETBUF MOVEM.L D0-D2/A6,-(SP) ;Save registers
GET00 MOVE.B ACKNAK.L,D0 ;Send handshake character (ACK or NAK)
BSR BYTEOUT ; to indicate we're ready to receive
MOVE.B #NAK,ACKNAK.L ;Assume the transmission will fail
GET10 BSR BYTEIN ;Read the first byte of the block
BEQ.S GET00 ;Send a NAK if we time out
CMPI.B #EOT,D0 ;Is it an EOT?
BNE.S GET15 ;Branch if not
ST EOTFLG ;Set EOT flag
BRA GET90 ; and exit
GET15
CMPI.B #SOH,D0 ;Loop until we get the start-of-heading
BNE.S GET10
BSR BYTEIN ;Get the block number
BEQ.S GET00 ;Send a NAK if we time out
MOVE.B D0,D1
BSR BYTEIN ;Get the complement of the block number
BEQ.S GET00 ;Send a NAK if we time out
NOT.B D0
CMP.B D1,D0
BNE.S GET00 ;NAK if they don't match
CMP.B INCNT.L,D0 ;Is this the block we expect?
BEQ.S GET20 ;Branch if yes
ADDQ.B #1,D0 ;Is this the previous block?
CMP.B INCNT.L,D0
BEQ.S GET17 ;Branch if yes
JSR VERROR ;Else fatal error
ASCII '402 - BAD XMODEM BLOCK NO.'
DC.B 0
BRA.S GET90 ;Exit
GET17
MOVE.W #127+1,D1 ;Init counter for 128 bytes + checksum
GET18 BSR BYTEIN ;Get the byte
BEQ.S GET00 ;Send a NAK if we time out, & start over
DBF D1,GET18 ;Loop for 128 bytes
MOVE.B #ACK,ACKNAK.L ;Indicate no errors
BRA GET00 ;Get next block
GET20
MOVE.W #127,D1 ;Init loop counter to receive 128 bytes
MOVEA.L #INBUFF,A6 ;Point to start of output buffer
CLR.B D2 ;Init checksum
GET50 BSR BYTEIN ;Get the byte
BEQ GET00 ;Send a NAK if we time out, & start over
ADD.B D0,D2 ;Update checksum
MOVE.B D0,(A6)+ ;Store byte into input buffer
DBF D1,GET50 ;Loop for 128 bytes
BSR BYTEIN ;Read in the checksum
BEQ GET00 ;Send a NAK if we time out, & start over
CMP.B D2,D0 ;Compare it to what we computed
BNE GET00 ;Branch if we failed
MOVE.B #ACK,ACKNAK.L ;Otherwise, indicate no errors and exit
ADDQ.B #1,INCNT.L ;Count this block
GET90 MOVEM.L (SP)+,D0-D2/A6 ;Restore registers
RTS
;-----------------------------------------------------------------------
;Send the output buffer out the RS-232 port
;
SENDBUF MOVEM.L D0-D2/A6,-(SP) ;Save registers
SEND00 MOVEQ #SOH,D0 ;Send the start-of-heading byte
BSR BYTEOUT
BSR FLUSH ;Flush out any bytes in the receive reg
MOVE.B OTCNT.L,D0 ;Send the block count
BSR BYTEOUT
NOT.B D0 ;Send its complement
BSR BYTEOUT
MOVE.W #127,D1 ;Init loop counter to send 128 bytes
MOVEA.L #OTBUFF,A6 ;Point to start of output buffer
CLR.B D2 ;Init checksum
SEND10 MOVE.B (A6)+,D0 ;Get byte from output buffer
ADD.B D0,D2 ;Update checksum
BSR BYTEOUT ;Send the byte
DBF D1,SEND10 ;Loop for 128 bytes
MOVE.B D2,D0 ;Send the checksum
BSR BYTEOUT
SEND20 BSR BYTEIN ;Wait for and get response
BEQ.S SEND20 ;Wait forever -- no timeout
CMPI.B #ACK,D0 ;If it is not an ACK then re-send
BNE.S SEND00
ADDQ.B #1,OTCNT.L ;Count this block
MOVEM.L (SP)+,D0-D2/A6 ;Restore registers
RTS
;======================================================================
;LOWEST LEVEL I/O ROUTINES:
;
SERDATR EQU $DFF018 ;Serial port data and status register
SERDAT EQU $DFF030 ;Serial port data register (write)
SERPER EQU $DFF032 ;Serial port period and control register
INTREQ EQU $DFF09C ;Interrupt request (status) bits
;----------------------------------------------------------------------
;Routine to initialize RS-232 I/O.
;
OPEN MOVE.W #185,SERPER.L ;Set 8 bits, 19200 baud
MOVE.W #$0800,INTREQ.L ;Clear "receive buffer full" status bit
RTS
;-----------------------------------------------------------------------
;Flush out any characters in the receive register.
;
FLUSH MOVE.L D0,-(SP) ;Save D0
FL10 MOVE.W SERDATR.L,D0 ;Read status and data
BTST #14,D0 ;Receive buffer full
BEQ.S FL90 ;Branch if not -- exit
MOVE.W #$0800,INTREQ.L ;Clear "receive buffer full" status bit
BRA.S FL10 ;Loop until all bytes flushed
FL90 MOVE.L (SP)+,D0 ;Restore D0
RTS
;-----------------------------------------------------------------------
;Get a byte from the RS-232 port and return it in D0.
; "EQ" status is returned if a timeout occurs.
;
BYTEIN MOVEM.L D1,-(SP) ;Save D1
MOVEQ #20,D1 ;Init timeout counter for about 10 seconds
SWAP D1 ;(Trick to save one whole word!)
BYIN10 MOVE.W SERDATR.L,D0 ;Read status and data
BTST #14,D0 ;Receive buffer full
BNE.S BYIN30 ;Branch if yes -- go read byte
SUBQ.L #1,D1 ;Else tick off the timeout counter
BNE.S BYIN10 ;Loop back if there is still time left
MOVEM.L (SP)+,D1 ;Restore D1 without changing status
RTS ;Timed out -- return with "EQ" status
BYIN30 MOVE.W #$0800,INTREQ.L ;Clear "receive buffer full" status bit
ANDI.L #$000000FF,D0 ;Return byte in D0
ANDI.B #$FB,CCR ;Make sure Z flag is clear (NE)
MOVEM.L (SP)+,D1 ;Restore D1 without changing status
RTS
;----------------------------------------------------------------------
;Routine to output the byte in D0 to an RS-232 port.
;
BYTEOUT MOVEM.W D0-D1,-(SP) ;Save low word of registers
BYO10 MOVE.W SERDATR.L,D1 ;Get serial port status
BTST #13,D1 ;Test transmitter buffer empty
BEQ.S BYO10 ;Branch if it is not empty
ANDI.W #$00FF,D0 ;Add start and stop bits to the
ORI.W #$0100,D0 ; data byte
MOVE.W D0,SERDAT.L ;Output data
MOVEM.W (SP)+,D0-D1 ;Restore registers
RTS
END EQU @-1 ;Address where this handler ends
IF END > $7EF00
; IF END > START +$400
ERROR -- FILE IS TOO LONG
ENDIF
;-----------------------------------------------------------------------
;Hook this handler into the device handler table
;
ORG 4 *DEVNUM +DEVTBL
DC.L XMODEM
END
--------------------------
;Hook this handler into the device handler table
;
ORG 4 *DEVNUM +DEVTBL
DC